package fun.yao.common.security;

import fun.yao.common.uitls.MsgUtil;

import javax.crypto.Cipher;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;

public class Cryptor
{

    /**
     * 对输入的字节数组进行MD5算法摘要计算
     * @param sSrcMsg 返回MD5摘要
     * @return
     */
    public static String md5(String sSrcMsg) {
        char hexdigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                'a', 'b', 'c', 'd', 'e', 'f' };
        try {
            byte[] bytes = MsgUtil.StringToBytes(sSrcMsg);
            MessageDigest mdtemp = MessageDigest.getInstance("MD5");
            mdtemp.update(bytes);
            byte[] md = mdtemp.digest();
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexdigits[byte0 >>> 4 & 0xf];
                str[k++] = hexdigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            return null;
        }
    }

    public static String md5Base64(String sSrcMsg, String sCharSet)
    {
        try {
            byte[] bytes = sSrcMsg.getBytes(sCharSet);

            MessageDigest mdtemp = MessageDigest.getInstance("MD5");
            mdtemp.update(bytes);
            byte[] md = mdtemp.digest();

            return DESede.base64Encode(md);
        } catch (Exception e) {
            return null;
        }
    }

    /*
     * 签名 alg: 签名算法 pk: 私钥对象 message: 明文
     */
    public static byte[] sign(String alg, PrivateKey pk, byte[] message)
            throws InvalidKeyException, NoSuchAlgorithmException,
            SignatureException {
        Signature s = Signature.getInstance(alg);
        s.initSign(pk);
        s.update(message);

        return s.sign();
    }

    /*
     * 签名验证 alg: 签名算法 pk：公钥 message: 明文 signature: 签名
     */
    public static boolean verify(String alg, PublicKey pk, byte[] message,
                                 byte[] signature) {
        try {
            Signature s = Signature.getInstance(alg);
            s.initVerify(pk);
            s.update(message);
            return s.verify(signature);
        } catch (Exception e) {
            return false;
        }
    }

    /*
     * 根据证书文件返回证书对象
     */
    public static java.security.cert.Certificate getCertificate(String certfile)
            throws CertificateException {
        InputStream in = null;
        try {
            in = new FileInputStream(certfile);
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(in);
            //cert.checkValidity();
            return cert;
        } catch (FileNotFoundException e) {
            throw new CertificateException("证书文件[" + certfile + "]不存在！");
        }finally{
            try {
                if ( in != null )
                    in.close();
            } catch (IOException e) {
            }
        }
    }

    /**
     * 返回X509证书
     */
    public static X509Certificate createX509Certificate(byte[] certData,
                                                        boolean check) throws CertificateException {
        InputStream in = new ByteArrayInputStream(certData);
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate cert = (X509Certificate) cf.generateCertificate(in);
        try {
            in.close();
        } catch (Exception e) {
        }
        if (check)
            cert.checkValidity();
        return cert;
    }

    /**
     * 返回证书主题
     */
    public static String getCertSubject(byte[] certData)
            throws CertificateException {
        return createX509Certificate(certData, false).getSubjectDN().getName();
    }

    /**
     * 返回证书发行者
     */
    public static String getCertificateIssuer(byte[] certData)
            throws CertificateException {
        return createX509Certificate(certData, false).getIssuerDN().getName();
    }

    /**
     * 验证证书
     */
    public static boolean verifyCert(byte[] certData, byte[] caData) {
        try {
            X509Certificate caCert = createX509Certificate(caData, false);
            X509Certificate cert = createX509Certificate(certData, true);
            cert.verify(caCert.getPublicKey());
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public static java.security.cert.Certificate getCertificate(byte[] certData, boolean check)
            throws CertificateException {
        return createX509Certificate(certData, check);
    }

    /**
     * 验证签名 alg: 算法 cert: 证书对象 message: 明文 signature: 签名
     */
    protected static boolean verify(String alg, java.security.cert.Certificate cert,
                                    byte[] message, byte[] signature) {
        try {
            Signature s = Signature.getInstance(alg);
            s.initVerify(cert);
            s.update(message);
            return s.verify(signature);
        } catch (Exception e) {
            return false;
        }
    }

    public static byte[] string2Bytes(String message) {
        int cnt = 0;
        for (int i = 0; i < message.length(); i++) {
            if ((message.charAt(i) & 0xff00) != 0) {
                cnt++;
                Integer.toHexString(message.charAt(i) & 0xffff);
            }
        }
        // 2-byte presentation?
        if (cnt == 0) {
            return message.getBytes();
        }

        try {
            // Unicode presentation?
            byte[] msg = message.getBytes("GBK");
            if (msg.length == (message.length() + cnt)) {
                return msg;
            }
        } catch (Exception e) {
        }

        // GBK presentation?
        byte[] msg = new byte[message.length() + cnt];
        for (int i = 0; i < msg.length;) {
            char c = message.charAt(i);
            if ((c & 0xff00) != 0) {
                msg[i++] = (byte) ((c >> 8) & 0xff);
            }
            msg[i++] = (byte) (c & 0xff);
        }
        return msg;
    }

    private final static byte[] hex = "0123456789ABCDEF".getBytes();

    private static int parse(char c) {
        if (c >= 'a') {
            return (c - 'a' + 10) & 0x0f;
        }
        if (c >= 'A') {
            return (c - 'A' + 10) & 0x0f;
        }
        return (c - '0') & 0x0f;
    }

    public static byte[] hexStringToBytes(String hexstr) {
        byte[] b = new byte[hexstr.length() / 2];
        int j = 0;
        for (int i = 0; i < b.length; i++) {
            char c0 = hexstr.charAt(j++);
            char c1 = hexstr.charAt(j++);
            b[i] = (byte) ((parse(c0) << 4) | parse(c1));
        }
        return b;
    }

    /*
     * 返回私钥 file: 私钥文件(JKS格式) pass: 私钥文件密码 alias: 私钥别名
     */
    public static PrivateKey getPrivateKey(String file, String pass, String alias)
            throws GeneralSecurityException, IOException
    {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file) ;
            char[] password = pass.toCharArray();
            KeyStore ks = KeyStore.getInstance("jks");
            ks.load(fis, password);
            return (PrivateKey) ks.getKey(alias, password);
        }catch (Exception e) {
            return null;
        }finally
        {
            if ( fis != null )
                fis.close();
        }
    }

    /*
     * 返回私钥 file: 私钥文件(PKCS12格式) pass: 私钥文件密码
     */
    public static PrivateKey getPrivateKey(String pkcs12FileName, String pass)
            throws GeneralSecurityException, IOException
    {
        FileInputStream fis = null;
        try
        {
            fis = new FileInputStream(pkcs12FileName);
            char[] password = pass.toCharArray();
            KeyStore ks = KeyStore.getInstance("PKCS12");
            ks.load(fis, pass.toCharArray());
            Enumeration<String> e = ks.aliases();
            if ( e.hasMoreElements() )
            {
                String alias = (String) e.nextElement();
                return (PrivateKey) ks.getKey(alias, password);
            }else
            {
                return null;
            }
        }finally
        {
            if ( fis != null )
                fis.close();
        }
    }

    /*
     * 返回公钥 pkcs12FileName:证书文件名  pass:密码
     */
    public static PublicKey getPublicKey(String pkcs12FileName, String pass)
            throws GeneralSecurityException, IOException
    {
        FileInputStream fis = null;
        try
        {
            fis = new FileInputStream(pkcs12FileName);
            KeyStore ks = KeyStore.getInstance("PKCS12");
            ks.load(fis, pass.toCharArray());
            Enumeration<String> e = ks.aliases();
            if ( e.hasMoreElements() )
            {
                String alias = (String) e.nextElement();
                Certificate cert = ks.getCertificate(alias);
                return cert.getPublicKey();
            }
            else{
                return null;
            }
        }finally
        {
            if ( fis != null )
                fis.close();
        }
    }

    /*
     *
     */
    public static String bytesToHexString(byte[] b) {
        byte[] buff = new byte[2 * b.length];
        for (int i = 0; i < b.length; i++) {
            buff[2 * i] = hex[(b[i] >> 4) & 0x0f];
            buff[2 * i + 1] = hex[b[i] & 0x0f];
        }
        return new String(buff);
    }

    /*
     *
     */
    public static String getCertID(InputStream is) {
        final String SEPS = ", ";
        final int COUNT = 7;
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
            String issuer = cert.getIssuerDN().toString();
            issuer = issuer.replaceFirst("EMAILADDRESS=", "E=");
            issuer = issuer.replaceFirst("ST=", "S=");
            String issuerbak = issuer;
            String[] elements = new String[COUNT];
            int i = 0;
            for (i = 0; i < COUNT; i++) {
                int index = issuerbak.indexOf(SEPS);
                if (index == -1) {
                    elements[i] = issuerbak;
                    break;
                }
                elements[i] = issuerbak.substring(0, index);
                issuerbak = issuerbak.substring(index + SEPS.length());
            }

            StringBuffer buffer = new StringBuffer(500);
            BigInteger sn = cert.getSerialNumber();
            buffer.append(sn.toString(16).toUpperCase());
            buffer.append("@");
            for (; i >= 0; i--) {
                buffer.append(elements[i]);
                if (i > 0) {
                    buffer.append(SEPS);
                }
            }

            return new String(buffer);
        } catch (Exception e) {
            return "";
        }
    }

    public static byte[] rsaEncrypt(byte[] src, Key key)
            throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(src);
    }

    public static byte[] rsaDescrypt(byte[] src, Key key)
            throws GeneralSecurityException {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

            cipher.init(Cipher.DECRYPT_MODE, key);

            return cipher.doFinal(src);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 加密黄金二级交易系统发送给客户端的报文
     * @param src  源未加密的报文
     * @param key  服务器私钥
     * @return
     * @throws IOException
     * @throws GeneralSecurityException
     */
    public static byte[] rsaEncryptGess(byte[] src,Key key) throws IOException,GeneralSecurityException
    {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        ByteArrayOutputStream daos = new ByteArrayOutputStream();
        for ( int i = 0 ; i < src.length ; i = i + 100 )
        {
            int len = 0;
            if ( ( i + 100 ) > src.length )
                len = src.length - i ;
            else
                len = 100;
            byte[] tmp = new byte[len];
            System.arraycopy(src, i, tmp, 0, tmp.length);
            daos.write(cipher.doFinal(tmp));
        }
        return daos.toByteArray();
    }

    /**
     * 解密黄金二级交易系统客户端发送的加密报文
     * @param src 从客户端接收一以的完整请求加密报文
     * @param key         服务器私钥
     * @return
     */
    public static byte[] rsaDescryptGess(byte[] src,Key key) throws IOException,GeneralSecurityException
    {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, key);

        ByteArrayOutputStream daos = new ByteArrayOutputStream();
        for ( int i = 0 ; i < src.length ; i = i + 128 )
        {
            byte[] tmp = new byte[128];
            System.arraycopy(src, i, tmp, 0, tmp.length);
            daos.write(cipher.doFinal(tmp));
        }
        return daos.toByteArray();
    }

    public static void main(String args[]) throws Exception {
        String str = "exch_code=4041&head={},json={}&check_code=0123456789abcdef";

        System.out.println(md5(str));
        System.out.println(md5Base64(str,"utf-8"));
    }
}

